package ar.edu.caece.compiler.grammar.factories;

import ar.edu.caece.compiler.error.ErrorManager;
import ar.edu.caece.compiler.error.ParsingException;
import ar.edu.caece.compiler.grammar.AbstractTokenVisitorFactory;
import ar.edu.caece.compiler.grammar.Recognizer;
import ar.edu.caece.compiler.grammar.TokenRecognizer;
import ar.edu.caece.compiler.grammar.TokenVisitor;
import ar.edu.caece.compiler.grammar.attributes.Attribute;
import ar.edu.caece.compiler.grammar.attributes.AttributeUtils;
import ar.edu.caece.compiler.grammar.attributes.BooleanAttribute;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeExpr;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeVariable;
import ar.edu.caece.compiler.grammar.nonterminals.ARRAYE;
import ar.edu.caece.compiler.grammar.nonterminals.EXPR;
import ar.edu.caece.compiler.grammar.terminals.FinArrayToken;
import ar.edu.caece.compiler.grammar.terminals.InicioArrayToken;

public class ARRAYEFactory extends AbstractTokenVisitorFactory {

	private static ARRAYEFactory INSTANCE;
	
	private EXPRFactory exprFactory;
	private TokenVisitor parseEXPR;
	private TokenVisitor parseFinArrayToken;
	
	public static ARRAYEFactory getInstance() throws Exception {
		if(INSTANCE==null) INSTANCE = new ARRAYEFactory();
		return INSTANCE;
	}
	
	
	private Initializer initializer = new Initializer() {
		
		@Override
		public void init() throws Exception {
			initializer = NoOperationInitializer.getInstance();
			exprFactory = EXPRFactory.getInstance();
			parseEXPR = exprFactory.makeVisitor();
			parseFinArrayToken = new FinArrayToken.FinArrayTokenVisitor() {
				
				@Override
				public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
					ErrorManager.getInstance().manage(llamador,"FinArrayToken",new ParsingException("Error de parseo. Se recibi�: " +llamador.getToken(),FinArrayToken.class,llamador));
					return null;
				}
				
				@Override
				public Recognizer finArrayCase(FinArrayToken llamador) {
					return llamador;
				}
			};
		}
	};
	
	private ARRAYEFactory() throws Exception {
	}
	
	@Override
	public TokenVisitor makeChainedVisitor(
			final TokenVisitor siguiente) throws Exception {
		initializer.init();

		return new InicioArrayToken.InicioArrayChainVisitor(siguiente) {
			
			@Override
			public Recognizer inicioArrayCase(InicioArrayToken llamador) throws Exception {
				
				parseEXPR.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA, getAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA));
				
				EXPR executeExpr = (EXPR) nextToken().execute(parseEXPR);
				FinArrayToken finArrayToken = (FinArrayToken) nextToken().execute(parseFinArrayToken);
				ARRAYE arraye = new ARRAYE(llamador, executeExpr, finArrayToken);
				arraye.addAttribute("es",new BooleanAttribute(true));
				arraye.addAttribute("tipo", executeExpr.getAttribute("tipo"));
				if(!"NATURAL".equalsIgnoreCase(arraye.getAttribute("tipo").toString())) {
					//TODO ERROR
					AttributeUtils.error("Error. Array declarado con variable de tipo no natural.",llamador);
				}
				
				NodeVariable var = new NodeVariable();
				var.setExpresion((NodeExpr) executeExpr.getAttribute(Attribute.ARBOL_SINTETIZADO));
				
				arraye.addAttribute(Attribute.ARBOL_SINTETIZADO,var);
				return arraye;
			}
		};
	
	}
	@Override
	public TokenVisitor makeVisitor() throws Exception {
		initializer.init();

		return new InicioArrayToken.InicioArrayTokenVisitor() {
			
			@Override
			public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
				ErrorManager.getInstance().manage(llamador,"ARRAYE",new ParsingException("Error de parsing.",ARRAYE.class,llamador));
				return null;
			}
			
			@Override
			public Recognizer inicioArrayCase(InicioArrayToken llamador) throws Exception {
				EXPR executeExpr = (EXPR) nextToken().execute(parseEXPR);
				FinArrayToken finArrayToken = (FinArrayToken) nextToken().execute(parseFinArrayToken);
				ARRAYE arraye = new ARRAYE(llamador, executeExpr, finArrayToken);
				arraye.addAttribute("es",new BooleanAttribute(true));
				if(!"NATURAL".equalsIgnoreCase(arraye.getAttribute("tipo").toString())) {
					//TODO ERROR
					AttributeUtils.error("Error. Array declarado con variable de tipo no natural.",llamador);
				}
				
				NodeVariable var = new NodeVariable();
				var.setExpresion((NodeExpr) executeExpr.getAttribute(Attribute.ARBOL_SINTETIZADO));
				
				arraye.addAttribute(Attribute.ARBOL_SINTETIZADO,var);
				return arraye;
			}
		};
	}
	
	
}
